package com.lz.ub

import com.felhr.usbserial.UsbSerialDevice
import com.lz.ub.core.*

/**
 * @author You
 * USB Command 指令执行器, 回调一些常用主板指令响应状态, 一般每个蓝牙主板发出一个指令都会响应一本地指令发送状态
 */
internal class UBCOperator(
    private val serialPort: UsbSerialDevice,
    private val dispatcher: UBCDispatcher,
    val openSuccess: Boolean
) : UBCEventCallback {

    companion object {

        const val TAG = "UBCOperator"

        //16进制转字节
        fun hex2byte(hex: String): ByteArray {
            if (hex.length < 2 || (hex.length and 1) == 1) return ByteArray(0)
            val byteArray = ByteArray(hex.length / 2)
            for (index in hex.indices step 2) {
                byteArray[index / 2] = Integer.parseInt(hex.substring(index, index + 2), 16).toByte()
            }
            return byteArray
        }
    }

    //支持的一些指令写入器
    private val writableArr = arrayOf(
        UBCRestart(this), UBCConnection(this), UBCAutoCancel(this),
        UBCServing(this), UBCWriter(this), UBCSimple(this)
    )

    //当前正在执行的执行写入器
    private var currentWritable = writableArr[0]

    //找出不可用的执行器
    private fun unAvailableWritable(): UBCWritable? {
        for (writable in writableArr) {
            if (!writable.isAvailable()) return writable
        }
        return null
    }

    fun isAvailable(): Boolean {
        if (openSuccess) {
            val unAvailableWritable = unAvailableWritable() ?: return true
            log("****** isUnAvailable name = ${unAvailableWritable.label} ******")
        }
        return false
    }

    fun log(log: String) {
        Logger.d(TAG, "......${dispatcher.id}......$log")
    }

    fun close() {
        serialPort.close()
        writableArr.forEach { it.close() }
    }

    fun restart() {
        writableArr.forEach { it.close() }
        Thread.sleep(COMMAND_RESTART_DELAYED)
        (writableArr[0] as UBCRestart).restart()
    }

    //推入连接设备
    fun pushConnectPendingDevices(devices: List<UBCDevice>) {
        (writableArr[1] as UBCConnection).pushPendingDevices(devices)
    }

    fun cancelAndClear() {
        writableArr.forEach { it.close() }
        Thread.sleep(COMMAND_RESTART_DELAYED)
        (writableArr[2] as UBCAutoCancel).cancel()
    }

    //推入待设置UUID的设备
    fun pushServingPendingDevices(devices: List<UBCDevice>) {
        (writableArr[3] as UBCServing).pushPendingDevices(devices)
    }

    fun pushServingPendingDevice(device: UBCDevice) {
        (writableArr[3] as UBCServing).pushPendingDevice(device)
    }

    //推入要写指令的设备及指令
    fun pushWritePendingDevices(devices: Collection<UBCDevice>, hexCommand: String, timeout: Int) {
        (writableArr[4] as UBCWriter).pushPendingDevices(devices, hexCommand, timeout)
    }

    fun pushWritePendingDevice(device: UBCDevice, hexCommand: String, timeout: Int) {
        (writableArr[4] as UBCWriter).pushPendingDevice(device, hexCommand, timeout)
    }

    //推入简单的指令
    fun pushSimpleCommand(command: String) {
        (writableArr[5] as UBCSimple).pushCommand(command)
    }

    //重启完成
    fun restartOver() {
        log("restartOver")
    }

    //每次连接完成都要尝试去设置一下Handler的值, 在每次onSuccess的时候也要去判断是否要设置handler的值
    fun connectOver() {
        log("connectOver")
    }

    fun disconnectOver() {
        log("connectOver")
    }

    fun servingOver() {
        log("servingOver")
    }

    fun writeOver() {
        log("writeOver")
    }

    private fun write(command: ByteArray, writable: UBCWritable): Boolean = try {
        currentWritable = writable
        serialPort.write(command)
        true
    } catch (_: Exception) {
        false
    }

    fun write(command: String, writable: UBCWritable): Boolean {
        log("write command $command, model = ${writable.label}")
        return write(command.toByteArray(), writable)
    }

    fun writeHex(command: String): Boolean {
        val writable = writableArr[4]
        log("write hex command $command, model = ${writable.label}")
        return write(hex2byte(command), writable)
    }

    internal fun postDelayed(r: Runnable, delayMillis: Long) {
        dispatcher.h.postDelayed(r, delayMillis)
    }

    internal fun removeCallbacks(r: Runnable) {
        dispatcher.h.removeCallbacks(r)
    }

    override fun onRestart() {
        if (currentWritable is UBCRestart) {
            val restart = currentWritable as UBCRestart
            if (restart.isExecuting()) {
                restart.restartCallback()
            } else {
                log("device attach restart callback ******")
                restart.restart()
            }
        } else {
            log("onRestart but currentWritable is not UBCRestart")
        }
    }

    override fun onOK() {
        log("onOk ${currentWritable.label}")
        currentWritable.responseOk()
    }

    override fun onError() {
        log("onError ${currentWritable.label}")
        currentWritable.responseFail()
    }

    override fun onConnected(mac: String, id: Int) {
        log("onConnected mac = $mac, link = $id")
        dispatcher.onConnected(mac, id)
    }

    override fun onSuccess() {
        log("onSuccess ${currentWritable.label}")
        if (currentWritable is UBCWriter || currentWritable is UBCAutoCancel) return
        dispatcher.serving()
    }

    override fun onService() {
    }

    override fun onDisconnected(mac: String) {
        log("onDisconnected mac = $mac")
        //这里的断开一律按自动断开处理
        dispatcher.onDisconnected(mac)
    }

    override fun onWrite(size: Int) {
        if (currentWritable is UBCWriter) {
            log("onWrite size = $size")
            (currentWritable as UBCWriter).writeCallback(size)
        } else {
            log("onWrite but current writable is not UBCWriter")
        }
    }

    override fun onReceived(id: Int, data: String) {
        log("onReceived id = $id, data = $data")
        dispatcher.onReceived(id, data)
    }

    override fun onReceivedHex(id: Int, bytes: ByteArray, start: Int, size: Int) {
        log("onReceived hexData id = $id, start = $start, size = $size")
        dispatcher.onReceivedHex(id, bytes, start, size)
    }

    override fun onScan(result: UBCScanResult) {
        log("onScan result = $result")
        dispatcher.onScan(result)
    }
}